const WebSocket = require("ws");


function createWebSocketServer(options) {

    const clientList = [];
    const heartTime = 10 * 1000;
    let idInex = 0;
    class SocketClient {
        constructor(type, socket, name) {
            idInex++;
            this.type = type;
            this.typeN = (type === 'player' ? 0: 1);
            this.socket = socket;
            this.id = idInex;
            this.selectPlayer = 0;
            this.start = 0;
            this.active = 0;
            this.sleeping = 0; 
            this.lastMsgTime = Date.now();

            this.name = name;
            socket.on('message', this.onMessage.bind(this))
            socket.on('close', this.onClose.bind(this));
            socket.on('error', this.onError.bind(this));

        }
        setSleeping(sleeping, toBrocast) {
            if (sleeping == this.sleeping) {
                return false;
            }
            this.sleeping = sleeping; 
            if (toBrocast) {
                this.updatePlayerList(); 
            }
            return true;
        } 
        onAdded() {

            let msgList = [
                {
                    type: 'onReady',
                    data: {
                        id: this.id,
                        type: this.type
                    }
                }
            ];
            brocast({
                type: 'msgList',
                data: msgList
            }, [this]);
            this.updatePlayerList();
            // console.log('onAdded ', this.type, this.name);
        }
        updatePlayerList() {
            brocast({
                type: 'playerList',
                data: getPlayerInfoList()
            }, getProfilerClients());

        } 
        onClose() {
            this.dispose();
            this.updatePlayerList(); 
            updatePlayerActive();
            // console.log('onClose ', this.type, this.name); todo not open
        }
        onError() {
            console.log('onError ', this.type, this.name);
        }

        onMessage(dataStr) { 
            let msg = JSON.parse(dataStr);
            if (msg.type === 'heart') { 
                this.lastMsgTime = Date.now(); 
                if (this.sleeping) {
                    this.setSleeping(0, true);
                }
                return;
            }
            this.lastMsgTime = Date.now(); 
            if (this.sleeping) {
                this.setSleeping(0, true);
            }            
            if (msg.type === 'selectPlayer') {
                let playerClient = getPlayerClientById(msg.data.id);
                // console.log(dataStr, msg.data.id, !!playerClient);
                if (playerClient) {
                    // this.selectPlayer = msg.data.id; 
                    this.sendMsg({
                        type: 'selectPlayer_back',
                        data: {
                            id: msg.data.id,
                            code: 0
                        }
                    }); 
                    brocast({
                        type: 'onSelectMe',
                        data: {
                            selectPlayer: msg.data.id,
                            profilerId: this.id 
                        }
                    }, [playerClient]);
                } else {
                    this.sendMsg({
                        type: 'selectPlayer_back',
                        data: {
                            code: 1
                        }
                    });
                }
            } else if ('onSelectMe_back' === msg.type) {
                let profilerClient = getPlayerClientById(msg.data.profilerId);
                if (profilerClient) {
                    profilerClient.selectPlayer = this.id; 
                }
                // console.log(msg.type, !!profilerClient, msg.data.profilerId, msg);

            } else if (msg.type === 'start') {
                this.start = 1;

                if (this.type === 'player') {

                    this.updatePlayerList();
                }
                this.sendMsg({
                    type: 'start_back',
                    data: {
                        code: 0
                    }
                }); 
                updatePlayerActive();
                if (this.type === 'profiler') { 
                    
                    let playerClient = getPlayerClientById(this.selectPlayer);
                    if (playerClient) { 
                        playerClient.sendMsg({
                            type: 'getPerformanceConf'
                        });
                    }
                }
                return;
            } else if (msg.type === 'stop') {
                this.start = 0;
                if (this.type === 'player') {

                    this.updatePlayerList();
                }
                this.sendMsg({
                    type: 'stop_back',
                    data: {
                        code: 0
                    }
                });
                
                updatePlayerActive();
                return;
            }
            if (msg.toId) {
                let toClient = getPlayerClientById(msg.toId);
                if (toClient) {
                    toClient.sendMsg(msg);
                }
                return;
            }
            if (this.type === 'player') {
                let targets = getPlayerClientsBySelectPlayer(this.id);
                if (targets.length > 0) {
                    brocast(msg, targets);
                }  
            } 
            
        }
        sendMsg(data) {
            if (this.socket && this.socket.readyState) {
                this.socket.send(JSON.stringify(data));
            }
        }
        dispose() {
            removeClient(this.socket);
            this.socket = null;
        }
    }

    function getPlayerClientsBySelectPlayer(selectPlayer) {
        let list = [];
        for (let i = 0; i < clientList.length; i++) {
            let info = clientList[i];
            if (info.start == 1 && info.selectPlayer === selectPlayer) {
                list.push(info);
            }
        }
        return list;
    }
    function getPlayerClientById(id) {
        for (let i = 0; i < clientList.length; i++) {
            let info = clientList[i];
            if (info.id === id) {
                return info;
            }
        }
        return null;
    }
    function getPlayerClients() {
        let list = [];
        for (let i = 0; i < clientList.length; i++) {
            let info = clientList[i];
            if (info.type === 'player') {
                list.push(info);
            }
        }
        return list;
    }
    function getProfilerClients() {
        let list = [];
        for (let i = 0; i < clientList.length; i++) {
            let info = clientList[i];
            if (info.type === 'profiler') {
                list.push(info);
            }
        }
        return list;
    }
    function getPlayerInfoList() {
        let clients = getPlayerClients();
        let list = [];
        for (var i = 0; i < clients.length; i++) {
            let info = clients[i];
            if (info.socket && info.socket.readyState === 1 && info.start == 1) {
                list.push(
                    {
                        type: info.type,
                        id: info.id,
                        name: info.name,
                        sleeping: info.sleeping
                    }
                );
            }
        }
        return list;

    }
    function brocast(data, clients) {
        if (!clients) {
            clients = clientList;
        }
        for (let i = 0; i < clients.length; i++) {
            let info = clients[i];
            info.sendMsg(data);
        }
    }
    function getClientIndex(socket) {
        for (let i = 0; i < clientList.length; i++) {
            let info = clientList[i];
            if (info.socket === socket) {
                return i;
            }
        }
        return -1;
    }
    function addClient(socket, type, name) {
        let index = getClientIndex(socket);
        if (index === -1) {
            let client = new SocketClient(type, socket, name);
            clientList.push(client);
            client.onAdded();
        }
    }

    function removeClient(socket) {
        let index = getClientIndex(socket);
        if (index >= 0) {
            clientList.splice(index, 1);
        }
    }
    const getParameterByName = function (name, url) {
        name = name.replace(/[\[\]]/g, '\\$&');
        var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
            results = regex.exec(url);
        if (!results) return null;
        if (!results[2]) return '';
        return decodeURIComponent(results[2].replace(/\+/g, ' '));
    };
    // 启动server
    if (!options) {
        options = {};
    }
    if (!options.port) {
        options.port = 1050;
    }

    let wss = new WebSocket.Server({ port: options.port });
    wss.on('error', function (error) {
        if (options.onError) {
            options.onError(error);
        }
    });

    wss.on('close', function (server) {
        if (options.onClose) {
            options.onClose(server);
        }
    });
    wss.on('listening', function (server) {
        if (options.onListening) {
            options.onListening(server);
        }
    });
    wss.on('connection', function (w, req) {
        // console.log("w:", w);
        let type = getParameterByName('type', req.url);
        let userAgent = '';
        userAgent = req.headers['user-agent'] || '';
        let name = '';
        let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || '';
        if (userAgent) {
            let index1 = userAgent.indexOf('(');
            let index2 = userAgent.indexOf(')');
            if (index1 >= 0 && index2 >= 0) {
                let arr = userAgent.substring(index1 + 1, index2).split(';');
                for (var i = 0; i < 3 && i < arr.length; i++) {
                    name += arr[i];
                }
            }
        }
        ip = ip.replace('::ffff:', '');
        ip = ip.replace('::1', 'localhost');
        if (ip) {
            ip = '' + ip + ':' + req.connection.remotePort;
        }
        name = ip + " " + name;
        addClient(w, type, name);
        // console.log(w);
    });
    function updatePlayerActive()  { 
        let len = clientList.length;  
        let selectPlayerList = [];
        for(let i = 0; i < len; i ++) {
            let client = clientList[i];
            // type == profiler
            if (client.typeN === 1) {
                if (client.start && client.selectPlayer > 0) { 
                    selectPlayerList.push(client.selectPlayer);
                } 
            } 
        } 
        for(let i = 0; i < len; i ++) {
            let client = clientList[i];
            // type == player
            if (client.typeN === 0) {
                let active = selectPlayerList.indexOf(client.id) >= 0? 1: 0;
                if (active != client.active) {
                    client.active = active; 
                    brocast({
                        type: 'active',
                        data: {active: active}
                    }, [client]);
                } 
            } 
        } 
    }
    // 检测心跳
    const heartIntervalHandler = setInterval(() => { 
        let len = clientList.length;
        let nT = Date.now();
        let dt = 0;
        let changed = false; 
        for(let i = 0; i < len; i ++) {
            let client = clientList[i];
            // type == player
            if (client.typeN === 0) {
                dt = nT - client.lastMsgTime;
                if (dt >= heartTime) {
                    if (client.setSleeping(1)) {
                        changed = true;
                    };
                }
            } 
        }
        if (changed) { 
            brocast({
                type: 'playerList',
                data: getPlayerInfoList()
            }, getProfilerClients());
        }
    }, 1000 * 10);
    window.wwss = wss;
    return {
        close: function (cb) {
            clearInterval(heartIntervalHandler);
            if (wss) { 
                wss.close(cb);
                wss = null;
            }
        }
    }
}
exports.createWebSocketServer = createWebSocketServer;